import { Injectable, ConflictException, NotFoundException, BadRequestException, ForbiddenException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { User } from './user.entity';
import { CreateUserDto, UpdateUserDto, QueryUserDto, LoginDto, ChangePasswordDto, FindPasswordDto } from './user.dto';
import * as bcrypt from 'bcryptjs';
import { JWT_EXPIRES_IN, JWT_REMEMBER_EXPIRES_IN } from './user.constants';

/* 
密码加密时打开
1. create（） 
password: hashedPassword, // 返回的密码会加密，暂时关闭，需要时可打开


2. update()
密码更新时重新加密， // 返回的密码会加密，暂时关闭，需要时可打开以下代码
if (updateUserDto.password) {
  const salt = await bcrypt.genSalt(10);
  updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
}

3. login()
const isPasswordValid = await bcrypt.compare(password, user.password);  // 密码加密的话需要这行代码

4. changePassword()
const isVerifySuccess = await bcrypt.compare(oldPassword, user.password);
const hashedNewPwd = await bcrypt.hash(newPassword, salt);

5. findPassword()
const hashedNewPwd = await bcrypt.hash(newPassword, salt);
 */

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private jwtService: JwtService,
  ) { }

  // 创建用户（直接存储userImage URL）
  async create(createUserDto: CreateUserDto): Promise<User> {
    // 检查账号是否已存在
    const existingUser = await this.userRepository.findOne({
      where: { username: createUserDto.username },
    });
    if (existingUser) {
      throw new ConflictException('账号已存在');
    }

    // 密码加密
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(createUserDto.password, salt);

    // 直接使用前端传递的userImage URL（可选）
    const user = this.userRepository.create({
      ...createUserDto,
      // password: hashedPassword, // 返回的密码会加密，暂时关闭，需要时可打开
      // userImage 若存在则存储，不存在则为null
    });
    return this.userRepository.save(user);
  }

  // 分页查询用户（支持搜索）
  async findAll(query: QueryUserDto): Promise<{ data: User[]; total: number }> {
    const { keyword, page = 1, limit = 10 } = query;
    const skip = (page - 1) * limit;

    const queryBuilder = this.userRepository.createQueryBuilder('user');
    if (keyword) {
      queryBuilder.andWhere('user.username LIKE :keyword', {
        keyword: `%${keyword}%`,
      });
    }

    const [data, total] = await queryBuilder
      .skip(skip)
      .take(limit)
      .orderBy('user.createdAt', 'DESC')
      .getManyAndCount();

    return { data, total };
  }

  // 根据ID查询用户
  async findOne(id: string): Promise<User> {
    const user = await this.userRepository.findOne({ where: { id } });
    if (!user) {
      throw new NotFoundException('用户不存在');
    }
    return user;
  }

  // 更新用户（支持更新userImage URL）
  async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);

    // 检查账号唯一性（若更新账号）
    if (updateUserDto.username && updateUserDto.username !== user.username) {
      const existingUser = await this.userRepository.findOne({
        where: { username: updateUserDto.username },
      });
      if (existingUser) {
        throw new ConflictException('新账号已存在');
      }
    }

    // 密码更新时重新加密， // 返回的密码会加密，暂时关闭，需要时可打开以下代码
    // if (updateUserDto.password) {
    //   const salt = await bcrypt.genSalt(10);
    //   updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
    // }

    // 直接更新userImage（若传递则更新，否则保持原数据）
    await this.userRepository.update(id, updateUserDto);
    return this.findOne(id);
  }

  // 删除用户
  async remove(id: string): Promise<void> {
    const user = await this.findOne(id);
    await this.userRepository.remove(user);
  }

  // 启用/禁用账号
  async toggleStatus(id: string, isEnabled: boolean): Promise<User> {
    await this.userRepository.update(id, { isEnabled });
    return this.findOne(id);
  }

  // ========== 新增核心功能 ==========
  /**
   * 用户登录
   * @param loginDto 登录参数
   * @returns token + 用户基本信息
   */
  async login(loginDto: LoginDto): Promise<{ token: string; user: Partial<User> }> {
    const { username, password, rememberMe } = loginDto;
    // 1. 查询用户
    const user = await this.userRepository.findOne({ where: { username } });
    if (!user) {
      throw new NotFoundException('账号不存在');
    }
    // 2. 检查账号状态
    if (!user.isEnabled) {
      throw new ForbiddenException('账号已被禁用，请联系管理员');
    }
    // 3. 验证密码
    // const isPasswordValid = await bcrypt.compare(password, user.password);  // 密码加密的话需要这行代码
    const isPasswordValid = user.password === password; // 目前密码未加密，直接对比
    if (!isPasswordValid) {
      throw new BadRequestException('密码错误');
    }
    // 4. 生成JWT Token
    const payload = { sub: user.id, username: user.username };
    const expiresIn = rememberMe ? JWT_REMEMBER_EXPIRES_IN : JWT_EXPIRES_IN;
    const token = this.jwtService.sign(payload, { expiresIn });

    // ========== 关键修改：同步更新数据库的 rememberMe + currentToken ==========
    await this.userRepository.update(user.id, {
      currentToken: token,
      rememberMe: rememberMe, // 将前端传入的 rememberMe 写入数据库
    });

    // 5. 重新查询用户（获取更新后的 rememberMe 值）
    const updatedUser = await this.userRepository.findOne({ where: { id: user.id } });

    // // 5. 记录当前登录token（用于退出登录）
    // await this.userRepository.update(user.id, { currentToken: token });
    // 6. 返回数据（隐藏密码等敏感信息）
    const { password: _, resetCode, resetCodeExpire, ...userInfo } = updatedUser;
    return { token, user: userInfo };
  }

  /**
   * 退出登录
   * @param userId 用户ID
   * @param token 当前token（可选，验证是否为当前登录token）
   */
  async logout(userId: string, token?: string): Promise<{ message: string }> {
    const user = await this.findOne(userId);
    // 验证token（防止退出他人登录）
    if (token && user.currentToken !== token) {
      throw new BadRequestException('无效的登录态');
    }
    // 清空当前token
    await this.userRepository.update(userId, { currentToken: null });
    return { message: '退出登录成功' };
  }

  /**
  * 简化版：修改密码（支持两种方式：1.旧密码验证 2.手机号/邮箱验证）
  * @param userId 用户ID
  * @param changePasswordDto 改密参数
  */
  async changePassword(userId: string, changePasswordDto: ChangePasswordDto): Promise<{ message: string }> {
    const { oldPassword, phone, email, newPassword } = changePasswordDto;
    const user = await this.findOne(userId);

    // 验证方式二选一：旧密码 OR 手机号/邮箱
    let isVerifySuccess = false;
    if (oldPassword) {
      // 方式1：旧密码验证
      // isVerifySuccess = await bcrypt.compare(oldPassword, user.password);
      isVerifySuccess = user.password === oldPassword; // 目前密码未加密，直接对比
      if (!isVerifySuccess) {
        throw new BadRequestException('旧密码错误');
      }
    } else if (phone || email) {
      // 方式2：手机号/邮箱验证（匹配绑定信息）
      if (phone && user.phone === phone) isVerifySuccess = true;
      if (email && user.email === email) isVerifySuccess = true;
      if (!isVerifySuccess) {
        throw new BadRequestException('手机号/邮箱与账号绑定信息不符');
      }
    } else {
      throw new BadRequestException('请输入旧密码或绑定的手机号/邮箱进行验证');
    }

    // 加密新密码并更新
    const salt = await bcrypt.genSalt(10);
    // const hashedNewPwd = await bcrypt.hash(newPassword, salt);
    const hashedNewPwd = newPassword; // 目前密码未加密，直接存储
    await this.userRepository.update(userId, {
      password: hashedNewPwd,
      currentToken: null, // 强制重新登录
    });
    return { message: '密码修改成功，请重新登录' };
  }

  /**
   * 简化版：找回密码（仅验证手机号/邮箱与账号绑定信息一致）
   * @param findPasswordDto 找回参数
   */
  async findPassword(findPasswordDto: FindPasswordDto): Promise<{ message: string }> {
    const { username, phone, email, newPassword } = findPasswordDto;
    // 1. 查询用户
    const user = await this.userRepository.findOne({ where: { username } });
    if (!user) {
      throw new NotFoundException('账号不存在');
    }
    // 2. 验证手机号/邮箱（二选一，匹配绑定信息）
    if (!phone && !email) {
      throw new BadRequestException('请输入绑定的手机号或邮箱');
    }
    if (phone && user.phone !== phone) {
      throw new BadRequestException('手机号与账号绑定信息不符');
    }
    if (email && user.email !== email) {
      throw new BadRequestException('邮箱与账号绑定信息不符');
    }
    // 3. 加密新密码并更新
    const salt = await bcrypt.genSalt(10);
    // const hashedNewPwd = await bcrypt.hash(newPassword, salt);
    const hashedNewPwd = newPassword; // 目前密码未加密，直接存储
    await this.userRepository.update(user.id, {
      password: hashedNewPwd,
      currentToken: null, // 强制重新登录
    });
    return { message: '密码找回成功，请使用新密码登录' };
  }
}